<?php
/**
 * Created by PhpStorm.
 * User: ford
 * Date: 14-9-4
 * Time: 下午2:24
 */
?>
<!DOCTYPE html>
<html lang="zh">
<head>
	<meta charset="UTF-8">
	<base href="http://localhost/notebook/php_advanced_and_object_oriented_programming_visual_quickpro_guide/" />
	<title>深入理解PHP——高级技巧、面向对象与核心技术</title>
	<link href="../css/bootstrap.css" rel="stylesheet">
	<style>
		article{
			background: -moz-linear-gradient(top, #fb0, #f50);
		}
	</style>
</head>
<body>
<article>
	<header>
		<h1>第1章 高级PHP技巧</h1>
	</header>
	<section>
		<p>对数组进行排序可用三个函数，usort()/uasort()/uksort()，它们接收两个参数，第一个参数是数组变量名，第二个参数是自定义的排序函数名。自定义排序函数有两个参数，即要排序的两个数，其返回值为负数或false时表示第一个参数在第二个前面，为0时表示相等，为正数或true时表示第一个参数排在后面。PHP会自动将数组中的值发送给排序函数进行排序。如果对多维数组排序，只需在自定义排序函数中加入数组维度下标即可。usort函数根据数值进行排序，但是它不保存键名（对外层数据来说的数组索引名称），uasort函数可以保存键名。uksort函数可以基于键名排序。</p>
		<p>使用$_SERVER['REQUEST_METHOD']=='POST'或'GET'来辅助判断是否有任务提交。</p>
		<pre>
		<?php
			$a = [['key1' => 940, 'key2' => 'blash'], ['key1' => 23, 'key2'=>'this'], ['key1' => 894, 'key2' =>'that']];
			echo '<br />用第一键排序前的数组<br />';
			print_r($a);
			//var_dump($a);

			/**
			 * 数组排序函数，用第一个关键字的值来排
			 * @param $x
			 * @param $y
			 * @return bool|int
			 */
			function asc_number_sort($x, $y)
			{
				if($x['key1'] > $y['key1'])
				{
					return true;
				}
				elseif($x['key1'] < $y['key1'])
				{
					return false;
				}
				else
				{
					return 0;
				}

			}

			//排序函数调用自定义排序方法
			usort($a, 'asc_number_sort');

			echo '<br />用第一键排序后的数组<br />';
			print_r($a);
			//var_dump($a);

			/**
			 * 按第二锓排序
			 * @param $x
			 * @param $y
			 * @return int
			 */
			function string_sort($x, $y)
			{
				return strcasecmp($x['key2'], $y['key2']);	//或者用区分大小写的strcmp函数来排序

			}

			usort($a, 'string_sort');

			echo '<br />用第二键排序后的数组';
			print_r($a);

		?>
		</pre>
		<p>有点意义的<a href="sort.php">另一个示例</a>。</p>
		<p>接下来是个关于<a href="modules.php">模块管理的示例</a>。<a href="add_task.php">这是一个用数据库记录产生多维数组的示全</a>，包括排序和嵌套函数。</p>
		<p>函数的递归调用，由于函数要一层层进入下一级调用，而第一次调用要等到其他调用结束时才会结束。因此，如果层次很深的话，会引起死机。有时候（不确定有多少次嵌套的时候）使用递归是唯一方案，但是使用循环更高效。</p>
		<p>使用递归要注意两件事：确保函数有一个退出语句，即达到某个条件将终止调用；每次调用函数都会消耗内存和处理器，递归调用将在全部递归完成后才会释放资源。</p>
		<p>PHP5.3开始支持匿名函数，为了方便之后引用，可以将它赋值给一个变量（调用时，只需在变量名后加()就可以了，如下例可这样调用：$hello('world');）。定义方式如下:</p>
		<pre>
			$hello = function($who)
			{
				echo '<p>Hello, $who</p>';
			}
		</pre>
		<p>还可以将一个函数作为另一个函数的参数进行调用，甚至直接在函数的参数表中定义匿名函数也是可以的，这样匿名函数将被直接使用。使用匿名函数重新索引数组。<a href="sort.php">查看sort.php示例。</a></p>
		<p><a href="view_task.php">一个递归数组的示例，</a>这个很好，仔细看看。</p>
		<p>静态变量的最好示例，递归函数。在出了一个函数进入下一次函数调用时变量仍保有其值或指针位置。</p>
		<p>匿名函数是PHP5.3中新增，在5.4中进一步扩展的。一些只使用一次的函数可以直接用匿名函数。使用引用传递（不常见）的一个好处是参数变量很大的时候不必再创建一个参数副本了。</p>
		<p>sprintf()可以有效的将SQL查询和使用的数据分离，还可以集成函数调用。它与printf的区别是后者是打印输出的，它并不产生输出，只是生成一个字符串。比如：</p>
		<pre>
			$q = sprintf("INSERT INTO tasks(parent_id, task) VALUES(%d, '%s'),$parent_id, mysqli_real_escape_string($dbc, strip_tags($_POST['task'])));
		</pre>
		<p>从参数上来说，sscanf()（用于读取输入，应当是用于命令行的吧）和fscanf()（用于从文件读取数据）分别对应printf和sprintf。</p>
	</section>
	<footer>
	</footer>
</article>
<article>
	<header>
		<h1>第2章 开发Web程序</h1>
		<section class="tips">
			<ul>
				<li>与安全相关的用户名及口令等数据最好保存在网站可访问的目录之外，并在配置文件里定义常量加以引用。比如数据库访问的配置文件，mysql.inc.php，应当将它保存在web目录之外，在config.inc.php中定义一个常量（比如DB）来指向它的绝对路径。这样，在需要使用数据库时只需要require(DB)即可。</li>
			</ul>
		</section>
	</header>
	<section>
		<p>高级的PHP程序员编写一系列具有单一功能的脚本，从而实现了模块化。而不是一个脚本完成多项任务，比如即显示表单又处理表单。</p>
		<p>每个Web程序都会由配置文件开始，网站的每个页面都有可能用到的信息就应该放在配置文件中。不是大部分页面都需要使用的函数放在单独的文件里。它一般有四个最重要的作用：</p>
		<ul>
			<li>定义常量</li>
			<li>建立站点范围内的设置</li>
			<li>创建用户自定义函数</li>
			<li>管理错误</li>
		</ul>
		<p>一个合适的站点结构应该可以提高站点的安全性和可管理性，同时也应该具有可伸缩性、可移植性并且便于修改。管理模块使用名字为admin的话，对从安全角度来说，这样不会给你带来任何好处。</p>
		<p>构建基于框架的网站常用的方式是用引导文件，即加载index文件，由它来实现具体内容的载入，事实上它是唯一会载入Web浏览器的页面。它只有一个作用，就是组合适当部分来形成完整的页面。按惯例来说，引导文件不会包含 任何HTML代码。涉及以下工作</p>
		<ul>
			<li>包含配置文件</li>
			<li>包含数据库链接文件</li>
			<li>合并HTML模板</li>
			<li>判断并包含适当的内容模块</li>
		</ul>
		<p>作为内容提供部分的模块文件不能让用户直接访问，可以通过检查config.inc.php中的常量来测试文件是否被直接访问。安全起见，可以自定义一个难猜的常量，不过一般没必要。</p>
		<p>需要注意，因为模块化设计带来的相对路径的问题，模块文件使用的相对路径，会因为嵌入到主程序中发生变化，成为错误路径。</p>
		<p>模块化的URL因为带有P参数显得不好看，因此，使用rewrite来解决这一点。apache的mod_rewrite使用正则表达式来处理复杂的链接地址模式。</p>
		<p>.htaccess文件就是一个文本文件，为了隐藏（Linux）在它前面加了.。要使它生效需要打开Apache服务器的rewrite模块，并修改配置文件httpd.conf（opensuse的配置位于/etc/apahce2/default-server.conf）中Directory块中的AllowOverride选项，它是用来定义.htaccess的作用的：</p>
		<ul>
			<li>AuthConfig 用于使用授权和认证</li>
			<li>FileInfo 用于执行重定义和URL地址重写</li>
			<li>Indexes 用于列举目录内容</li>
			<li>Limit 用于限制对目录的访问</li>
			<li>Options 用于设置目录行为，比如执行CGI脚本或者列举目录内容的能力</li>
			<li>All 全部</li>
			<li>None 无</li>
		</ul>
		<p>还要设置目录文件的Options属性，如下：</p>
		<pre>
			&lt;Directory "/srv/www/htdocs/workroom/bbs"&gt;
				Options FollowSymLinks
				AllowOverride All
			&lt;/Directory&gt;

		</pre>
		<p>另外，如果.htaccess文件没有写入权限的话，也会被忽略。这个文件的存在是为了保护止录下的文件，比如要阻止对一个目录内容的完全访问，可以创建以下的.htaccess文件内容：</p>
		<pre>
			#禁止浏览目录
			Options All -Indexes
			#防止目录列表
			IndexIgnore *
			#防止防问任何文件
			&lt;FilesMath "^.*$"&gt;
				Order Allow,Deny
				Deny from all
			&lt;/FilesMath&gt;
		</pre>
		<p>当然这只是阻止浏览器访问，php还是可以用include等命令来访问到目录下的内容的。对授权用户的访问限制最常用的是mod_auth模块，感兴趣可以查一下。</p>
		<p>.htaccess的书写规则如下：</p>
		<pre>
			#首先要检查mod_rewrite模块是否存在
			&lt;IfModule mod_rewrite.c&gt;
				#如果存在就打开重写引擎
				RewriteEngine on
				#下面就可以写规则了
				#语法是这样的：RewriteRule match rewrite，即在用户访问math文件（用正则表达式表示）的时候跳转到rewrite文件
			&lt;/IfModule&gt;
		</pre>
		<p>使用rewrite除去使链接更好看外，还利用SEO排名，因为标准名比带参数的值传递更突出。一旦使用了这个系统，就需要修改链接对应的URL地址，以及表单的action属性。另外，对于末尾带有/的地址，需要注意，相对地址会发生变化，所有相对链接地址都要改为绝对地址（使用HTML的base头元素应当也可以解决这个问题，正在测试中）！</p>
		<p>下面一节讲的是浏览器端的缓存更新。感觉不实用（一是更新太频会加重服务器负担，二是对不常更新的站点来说没什么用，三是判断本身也消耗服务器资源降低加载速度，可以使用HTML的META标签来影响页面缓存），用到时再作研究。</p>
	</section>
	<footer>
		<p>这一节讲的rewrite很好，一定要注意路径的问题。BASE标签终于有了大作用，很好！</p>
	</footer>
</article>
<article>
	<header>
		<h1>第3章 高级数据库概念</h1>
		<section class="tips">
		</section>
		<section class="modal-content">

		</section>
	</header>
	<section>
		<p>默认情况下，PHP将全部会话保存在服务器上的文本文件中，虽然这些文件以长串加密字符为文件名放在tmp目录下，但是所有网站会话都在一个目录下，可以很容易编写一个脚本从目录里读取会话文件夹所有文件的内容，因此，是非常不安全的。</p>
		<p>将会话数据保存在数据库里除了提高安全外还可以更方便地搜索到会话的更多信息，另外，如果网站运行于多台服务器上，则会话数据就不能被其他服务器访问，只能用数据库保存会话这个解决方案。</p>
		<p>要在数据库中保存会话信息，需要创建一个特殊的数据表，它至少包含三个字段：会话ID char(32)<strong>注意，在64位系统中，这个值是64,因此，设置为64为妥！</strong>、会话数据text（如果要保存大量数据，可以改为MEDIUMTEXT或LONGTEXT类型)、会话数据最后一次的访问时间TIMESTAMP。</p>
		<p>表创建好后，需要定义与数据库交互的函数，然后让PHP（通过session_set_save_handler()函数）来使用这些交互函数。session_set_save_handler()函数需要6个参数，每个参数都是一个自定义交互函数名，依次如下：</p>
		<ol>
			<li>启动会话调用的函数</li>
			<li>关闭会话调用的函数</li>
			<li>读取会话数据</li>
			<li>写入会话数据</li>
			<li>销毁会话数据</li>
			<li>删除旧的会话数据要调用的函数</li>
		</ol>
		<p>除了读取会话调用的函数返回字符串（可为空字符串）外，其他函数都必须返回一个BOOLEAN值。每次会话启动时，“打开”和“读取”函数会立即被调用。当读取函数被调用的时候，可能会发生垃圾回收过程（取决于多种因素)。脚本执行结束时，会话没有被销毁的情况下“写入”函数会被调用，然后就会调用“关闭”函数，接着是“销毁”函数。</p>
		<p>必须在session_start()执行前先执行session_set_save_handler()函数。所有输出被发送到浏览器后，“写入”会话函数才会被调用。如果PHP开启了session.auto_start，那么将无法使用session_set_save_handler()函数。</p>
		<p>在面向对象编程中，可以不用这样做，使用SessionHandler类即可，后面章节继续介绍。<a href="sessions.php">示例在此</a>。</p>
		<p>这一章下半部分是介绍用MySQL导入美国邮编列表（包括中心区域经纬度）并计算两点间距离，简单看下。这里面有个计算两点间距的公式，如果用到的话可以来查。还MySQL的存储函数，将计算两点间距的公式放在MySQL的存储程序中。关于存储过程的使用情境，还是去看MySQL的相关书籍吧，通过这个示例，感觉没什么大用。</p>
	</section>
	<footer>
		<p></p>
	</footer>
</article>
<article>
	<header>
		<h1>第4章 面向对象编程基础</h1>
		<section class="tips">
			<ul>
				<li></li>
				<li></li>
				<li></li>
				<li></li>
				<li></li>
			</ul>
		</section>
	</header>
	<section>
		<p>从技术角度讲，不一定要从代码里删除对象unset($object)；因为脚本一结束会被自然删除。但是，这样会使代码更有序一点。类名、方法名（PHP里的函数名）不区分大小写。</p>
		<p>当创建对象时，如果需要使用还没有被定义的类，__autoload()函数就会被调用。它的定义方式如下：</p>
		<pre>
			function __autoload($className) {
				require($class . '.php');	//这里就是类文件的绝对路径了，注意，PHP类名不匹分大小写，但是有些系统是区分大小写的，要注意文件名与类名的一致。
			}
		</pre>
		<p>使用UML设计类图时，属性如标识：userId:number = 0（如果有默认值的话就这样写上）;，方法如下标识：sayHello(language:string): void。任何地方如果需要使用已经模块化了的类，就可以在代码中直接使用，而不用去看类的实现代码。</p>
		<p>使用PHPDocumenter生成文档。注释应当使用PHPDocument要求的标准格式。PHPDocument文档块注注释的语法如下：</p>
		<pre>
			/**
			 * 短描述，只占一行
			 *
			 * 长描述，多行
			 * 以@开头的各种标记，比如：@author,@date,@copyright,@param等等，每个占一行
			 */
		</pre>
		<p>文档块注释可用于下面代码之前：</p>
		<ul>
			<li>类定义</li>
			<li>函数或方法定义</li>
			<li>变量声明</li>
			<li>常量定义</li>
			<li>文件包含</li>
		</ul>
		<p>文档的开始应当用文档块注释来说明文档的功能。类定义或函数块定义后应当用//说明定义结束。关于PHPDocument的完整用法可查看其<a href="http://www.phpdoc.org/docs/">官方文档</a>。 </p>
		<p>使用pear安装phpDocument的步骤如下：</p>
		<ol>
			<li>增加一个phpDocument PEAR通道：pear channel-discover pear.phpdoc.org</li>
			<li>安装phpDocument: pear install phpdoc/ phpDocumentor-alpha  （这个安装指令可能会有变化，随时查看官方文档）</li>
		</ol>
		<p>为一个php文件生成文档时可以这样：phpdoc -f HellowWorld.php -t docs，它的意思是解析当前目录下的HelloWorld.php文件，并在-t指定的目录docs下生成文档。</p>
		<p>如果要对整个当前目录生成文档，可以这样：phpdoc -d . -t docs。</p>
	</section>
	<footer>
		<p>这一章内容很简单，简单说了类定义，以及phpDocument。需要养成好的编程习惯，phpDocument的文档还是要认真看一下。</p>
	</footer>
</article>
<article>
	<header>
		<h1>第5章 高级面向对象编程技术</h1>
		<section class="tips">
			<ul>
				<li></li>
				<li></li>
				<li></li>
				<li></li>
				<li></li>
			</ul>
		</section>
	</header>
	<section>
		<p>UML图中，继承在两个类之间用空心三角箭头来表示，从子类指向父类。PHP中不存在重载函数，但是可以重写父类的方法，但是，尽量在一个类中修改其它的类。</p>
		<p>类要在独立的文件中存放，不要和使用它们的文件混在一起。PHP在创建子类对象的时候不会自动调用父类的构造函数，因此，如果需要初始化类，需要在子类构造函数中显式地调用父类的构造函数。或者某种情况下需要直接重写构造函数。如果不希望自己定义的方法被重写，那就在定义方法时前面加final关键字。</p>
		<p>要养成在PHP脚本结束前释放类变量的习惯，否则，析构函数中的内容将不会在页面中显示出来。</p>
		<p>类的私有/受保护成员仅能在类/类及其子类中访问，在类的实例中是不能访问的。比如，name属性，可以在类中查看，但是不应当在类外被修改，因此，将它定义为protected。</p>
		<p>UML图中+表示公有的public，-表示私有的private，#表示受保护的protected。作为惯例，私有变量名都是以一条下划线开始的，比如:private $_private。</p>
		<p>self被用来作为当前类的一个引用，比如：self::doThis();，这样用来区分父类和子类的方法，在子类中调用父类的方法则是这样：parent::doThis()。虽然在子类中也可以直接用“父类类名::方法名”这种方式来调用父类的方法，但可能会产生定义修改后的问题。</p>
		<p>用static定义的类变量能被所有实例访问（岂止是实例，非实例也行）。const定义的类常数可以被类或其子类的所有实例访问，但它的值不能改变，类常数也不可以用对象进行访问，只能用类名或在类方法中使用self或parent来进行访问。</p>
		<p>如果子类没有定义构造或析构函数，PHP会自动调用父类的构造或析构函数。静态方法应当都是public的，因为它们不能通过对象访问。$this引用的是当前对象，self引用的是当前类，因此前者不能用于静态方法和类常量的引用。</p>
	</section>
	<footer>
		<p>这一节讲的类的概念已经很熟悉了，很快看完。</p>
	</footer>
</article>
<article>
	<header>
		<h1>第6章 再谈高级面向对象编程</h1>
		<section class="tips">
			<ul>
				<li></li>
				<li></li>
				<li></li>
				<li></li>
				<li></li>
			</ul>
		</section>
	</header>
	<section>
		<p>抽象类是父类的模板，它定义类的一般行为，定义了它的继承类如何被使用，由它的子类负责实现。__toString()方法可以在对象被作为字符串使用时被自动调用，这个方法可以作为一个简单的调试工具。</p>
		<p>子类继承的方法的可见性必须等于或高于父类，不能比父类可见性更低。抽象类可以没有抽象方法，但只要有一个抽象方法，它就必须是抽象类。</p>
		<p>接口名字以i开头，但这不是必须的。接口中的所有方法可见性都为public，它只定义方法声明，没有属性。定义接口用关键字interface，要实现一个接口使用implements。类必须实现接口中的所有方法。</p>
		<p>比如，创建一个数据库操作的标准接口CRUD，即CreateReadUpdateDelete这四种常用的数据操作。</p>
		<p>一个接口解释了一个类或方法是如何被使用的，它可以指一个类需要绑定的合约。一个类可以有多个接口，只需用逗号分隔。instanceof操作符可用于测试一个类是否实现了一个接口。UML中接口通过前面加&lt;&lt;interfface&gt;&gt;表示，实现接口的类用虚线箭头指向接口。</p>
		<p>PHP5.4实现了一个代码复用的方法traits。它解决的问题是多个类拥有同样的方法，又不能从一个类中继承得到，就可以用trait来实现（因为PHP不支持多重继承）。traits的定义和类是一样的，只是用的关键字不同：</p>
		<pre>
			trait tSomeTrait {
				//定义几个属性

				function someFunction() {
					//方法的内容
				}
			}
		</pre>
		<p>traits名称以t开头以示区别。它不能被初始化，要想在类中使用定义的traits，只需要在类中使用use关键字来增加trait即可，这样类就立刻拥有了traits中定义的属性和方法，就和include一样：</p>
		<pre>
			class SomeClass {
				use tSomeTrait;
				//类的其它部分
			}
		</pre>
		<p>比如，我们可以定义一个调试用的traits，这样所有类都可以方便地使用它。它就像类定义中的PHP include一样，可以将已定义好的方法嵌入到类中使用。但是也不要滥用这种方式。可以用一条use语句包含多个trait，只需用逗号分隔。traits可以有抽象方法，这样，使用该trait的类就必须实现它的抽象方法。</p>
		<p>使用traits的时候，如果类中定义了同名的方法，则类中定义的方法优先级高于trait，如果类中的同名方法是继承来的（没有重写）那么trait中的优先级较高。</p>
		<p>UML中的组合使用一条线加一个三角形表示，从被包含的类（比如Employee)指向组合成的类（比如Department）。表示一个部门类有多个员工。</p>
		<p>类型提示是强类型语言中必须的，PHP7现在也实现了类型提示，5.3之后的版本支持对象参数类型的识别。如果一个函数的参数是一个对象类型，那么非此对象类型的参数将产生错误。</p>
		<p>PHP的命名空间就像是目录结构一样，将名称将同的变量放在不同目录下，就避免了名称冲突。命名空间可以用来存放：类/接口/函数/常量。命名空间还可以有子命名空间，使用\标示。可以在单个文件中定义多个命名空间，但不建议这样做。__NAMESPACE__常量代表当前命名空间。可以使用use将命名空间带入当前工作域。但通常不这样用。另外，命名空间的结构可以与目录结构对应，即一个空间对应一个目录。</p>
	</section>
	<footer>
		<p>对命名空间的讲述不是很详细，除非与他人协作，否则，用不到这类东东，具体情况查看PHP文档吧。</p>
	</footer>
</article>
<article>
	<header>
		<h1>第7章 设计模式</h1>
		<section class="tips">
			<ul>
				<li></li>
				<li></li>
				<li></li>
				<li></li>
				<li></li>
			</ul>
		</section>
	</header>
	<section>
		<p>一个设计模式就是解决某种特殊问题的推践的最佳实践。</p>
		<p>单例模式，确保特定类类型有且仅有一个实例，比如数据库链接类和config类。它的实现方法是在类中定义一个static私有变量来保存类的实例个数。如果类不存在就创建一个新的实例并返回：</p>
		<pre>
			class SomeClass {
				static private $_instance = null;

				static function getInstance() {
					if(self::$_instance == null) {
						self::$_instance = new SomeClass();
					}

					return self::$_instance;
				}
			}
		</pre>
		<p>通常情况下，单例模式的对象会使用全大写的变量名来表示。</p>
		<p>工厂模式和单例模式一样是另一种创建型模式，它可用于创建多种不同类型的多个对象。它用于以下情形：当在生成对象的时候不能确定其对象类型，只有在程序运行时才能确定。在动态的应用程序中，这种情况很常见。还有种方式，有一个抽象类，它的几个不同的派生子类需要立即被创建。</p>
		<p>工厂模式通常通过一个静态方法实现，一般情况下被命名为Create()(注，此处习惯上为大写），factory()，factoryMethod()或createInstance()，这个方法至少接收一个参数，用于标示将要创建的对象的类型，然后这个方法将返回该类型的一个对象。如：</p>
		<pre>
			static function Create($type) {
				//Validate $type
				return $new SomeClassType();
			}
		</pre>
		<p><a href="ShapeFactory.php">工厂模式的示例</a>，由于它的内部验证使得工厂类和应用程序的其他部分紧密耦合在一起。工厂模式的变种是抽象工厂模式，工厂模输出不同对象的对象，每一个都继承自同一个父类，而抽象工厂则输出这些工厂类。 </p>
		<p>当需要使用一个非传统的类结构或一个现有的类结构的修改版本的时候，需要引用组合模式。组合模式由一个抽象基类开始，实现共有的特征，而且这个基类有添加、删除、显示等处理自身元素（这些元素也就是组合的条目）的方法，它会被不同子类扩展，以减少重复编码。</p>
		<p>对于表单和表单中的元素来说，很多特定的形为是一致的，用组合模式提取出它们的组合关系和共性可以减少代码的重复，下面是个基类定义示例：</p>
		<pre>
			abstract class FormComponent {
				abstract function add(FormComponent $obj);
				abstract function remove(FormComponent $obj);
				abstract function display();
				abstract function validate();
				abstract function showError();
			}
		</pre>
		<p>下面是个子类示例：</p>
		<pre>
			/**
			 * 下面定义的是一个表单，它包含子元素，因此需要实现add方法
			 */
			class Form extends FormComponent {
				private $_elements = array();

				function add(FormComponent $obj) {
					$this->$_elements[] = $obj;
				}
				function display() {
					//Display the dntire form
				}
			}

			/**
			 * 下面定义的是一个表单元素，它没有子元素，不需要使用add方法，但是要实现抽象类，仍需定义add方法
			class FormElement extends FormComponent {
				function add(FormComponent $obj) {
					return $obj;	//这个add方法不需要做任何事，甚至返回false也是可以的
				}

				function display() {
					//显示元素的代码
				}
			}
		</pre>
		<p>使用上面这种定义方式就可以将表单对象和它里面的元素同样对待。追加一个示例，雇员和团队。工作可以分配给两者中的任何一个。以下是示例代码：</p>
		<pre>
			/**
			 * 下面是基类的定义，包括了团队和雇员的共性，以及基本的模式方法
			 */
			abstract class WorkUnit {
				protected $tasks = array();		//保存任务名
				protected $name = null;	//保存雇员或团队名

				function __construct($name) {
					$this->name = $name;
				}

				function getName() {
					return $this->name;
				}

				abstract function add(Employee $e);
				abstract function remove(Employee $e);
				abstract function assignTask($task);
				abstract function completeTask($task);
			}

			class Team extends WorkUnit {
				$private $_employees = array();	//保存团队成员

				function add(Employee $e) {
					$this->_employees[] = $e;
					echo "$e->getName() has been added to team $this->getName().";
				}

				function remove(Employee $e) {
					$index = array_search($e, $this->_employees);
					unset($this->_employees[$index]);
					echo "$e->getName() has been removed from team $this->getname().";
				}

				function assignTask($task) {
					$this->tasks[] = $task;
					echo "A new task has been assigned to team $this->getName(). It should be easy to do with $this->getCount() team members.";
				}

				function completeTask($task) {
					$index = array_search($task, $this->tasks);
					unset($this->tasks[$index]);
					echo "The '$task' task has been completed by team $this->getName().";
				}

				function getCount() {
					return count($this->_employees);
				}
			}

			class Employee extends WorkUnit {
				function add(Employee $e) {
					return false;
				}
				function remove(Employee $e) {
					return false;
				}
				function assignTask($task) {
					$this->tasks[] = $task;
					echo "A new task has been assigned to $this->getName(). It will be done by $this->getName() alone.";
				}

				function completeTask($task) {
					$index = array_search($task, $this->tasks);
					unset($this->tasks[$index]);
					echo "The '$task' task has been completed by employee $this->getName().";
				}
		</pre>
		<p>策略模式是行为类模式中的一个类型，行为类模式用来说明一个应用是如何运作的。工厂模式会改变对象的类型，策略模式改变的是算法。比如这个场景：我们需要一个字符串过滤系统，不同的系统需要包括：去除HTML多余的空格/过滤脏话/捕捉用来通过联系表单或类似的地方发布垃圾信息的字符组合。三种方式的共同点是都作用于字符串。因此，从一个基类继承可能是合理的，其次，过滤器应该立即生效。</p>
		<p>既然是算法，那就从接口开始，用接口来规定所需要的功能：</p>
		<pre>
			interface Filter{
				function filter($str);
			}
		</pre>
		<p>接下来用特定的过滤类型实现接口方法的特定版本</p>
		<pre>
			class HtmlFilter implements Filter {
				function filter($str) {
					//Strip out the HTML.
					return $str;
				}
			}

			class SwearFilter implements Filter {
				function filter($str) {
					//Cross out swear words.
					return $str;
				}
			}

		</pre>
		<p>最后，需要一个类来使用这些过滤器</p>
		<pre>
			class FormData {
				private $_data;

				function __construct($input) {
					$this->_data = $input;
				}

				function process(Filter $type) {
					$this->_data = $type->filter($this->_data);
				}
			}
		</pre>
	</section>
	<footer>
		<p>组合模式很不错，这种思路非常好。感觉策略模式就是接口的应用，并不能减少代码，只是对于规范代码有点用处，感觉没什么意义。</p>
	</footer>
</article>
<article>
	<header>
		<h1>第8章 使用既有的类</h1>
		<section class="tips">
			<ul>
				<li></li>
				<li></li>
				<li></li>
				<li></li>
				<li></li>
			</ul>
		</section>
	</header>
	<section>
		<p>面向过程的代码生成“错误”，而面向对象的代码生成“异常”Exception类包含以下方法，可以通过它们来获取关于错误的信息：</p>
		<ul>
			<li>getCode() 如果有，将返回收到的异常代号</li>
			<li>getMessage() 如果有，将返回收到的异常信息</li>
			<li>getFile() 返回生成异常的文件名</li>
			<li>getLine() 返回异常所在的代码行</li>
			<li>getTrace() 返回一个包含诸如文件名、行号等信息的数组</li>
			<li>getTraceAsString() 以字符串形式返回与getTrace()数组内容</li>
			<li>__toString() 字符串形式的前面所有数据</li>
		</ul>
		<p>try块中，一旦有异常抛出，其后的代码都不会执行。如果没有抛出异常catch块中的代码也永远不会执行。<a href="WriteToFile.php">一个写入文件的示例</a>。注意，使用fopen()函数时前面要添加错误抑制操作符，这样出问题时PHP本身不会报错，就达到了用异常处理的目的。使用try语句时必须保证能捕获所有异常，否则将导致严重错误。</p>
		<p>PHP中的Exception类十分简单，这样的目的是方便用户扩展它。注意，扩展时只构造函数和__toString方法是可以改写的，当然，也可以添加自定义方法用来写若干处理<a href="MyException.php">比如这个示例</a> 。在类定义中只管抛出异常，异常的处理放在执行代码的try...catch...块中。</p>
		<p>PHP5.1 之后内置了PDO提供一种一致的与数据库无关的数据库查询方式，这样，即使更改了数据库，只需要修改一行代码即可，不必修改里面的数据处理语句。要查看PDO当前支持哪些数据库，只需执行PDO::getAvailableDrivers()方法即可，它支持当前大多数流行数据库，如果没有可以添加驱动。当然，要使用PDO需要先打开PHP的PDO扩展。</p>
		<p>PDO类的构造函数有三个参数：$pdo = new PDO('dsn', 'username', 'password')。DSN是指名以下多条信息的字符串：</p>
		<ul>
			<li>要使用的数据库驱动</li>
			<li>数据库名</li>
			<li>如果使用SQLite，还需要提供数据库文件的路径</li>
			<li>主机名（可选）</li>
			<li>端口号（可选）</li>
		</ul>
		<p>也可以将用户名和密码放在DSN声明中，而不是作为实参传递。DSN的定义如下：首先是数据库驱动，后面跟冒号和以分号分隔名称=值对的参数表。比如连接本机的MySQL可以这样：</p>
		<pre>
			mysql:dbname=test;host=loccalhost
			sqlite:/path/to/somedb.sq3	注意，SQLite直接连接服务器上的数据文件，不需要用户名和密码
		</pre>
		<p>要关闭与数据库的连接，只需销毁PDO对象即可：unset($pdo);，或将它置为null：$pdo = null。</p>
		<p>作为一个类，PDO不产生错误而是抛出异常（PDOException类）。在使用PDO与数据库交互时，通常如下：</p>
		<pre>
			try {
				$pdo = new PDO('mysql:dbname=test;host=localhost', 'username', 'password');
				//执行查询
				//其他它码
			} catch(PDOException $e) {
				//使用 $e 和$e->getMessage()
			}
		</pre>
		<p><a href="add_task_pdo.php">PDO的应用示例</a>。记住，用PDO与数据库交互时，所有代码都要放到try块中。可以调用PDO的setAttribe方法向其传递合适的常数以调整报造数据库错误的等级。</p>
		<p>PDO执行不返回结果的简单查询只要执行$pdo->exec(SQL)，会返回影响的行数。对于insert查询可能过lastInsertId方法来得到生成的主键值。最后，执行简单查询之前应通过$pdo->quote($sql)来访止SQL注入攻击，它和调用mysqli_real_escape_string来防止注入是一样的。通过exec方法执行的查询没有影响任何数据时会返回0,出错时返回false。</p>
		<p>PDO执行返回记录的查询用$result = $pdo->query(SQL)，要查看返回的记录条数用$results->rowCount()（等价于mysqli_num_rows），$pdo->query()方法返回的是PDOStatement对象，要读取这个对象中的查询结果可使用它的fetch()方法，但是在使用fetch()方法之前需要先用setFetchMode()方法来设定读取记录的方式，有以下四种可选方式：</p>
		<ul>
			<li>PDO::FETCH_ASSOC 关联数组</li>
			<li>PDO::FETCH_NUM 数值索引的数组</li>
			<li>PDO::FTECH_OBJ 对象</li>
			<li>PDO::FETCH_CLASS 特定类型的对象</li>
		</ul>
		<p>示例代码：</p>
		<pre>
			$results = $pdo->query('SELECT id, username FROM users');
			$results->setFetchMode(PDO::FETCH_NUM);
			$while($row = $results->fetch()) {
				//使用$row[0]取得id字段的值
				//使用$row[1]取得username字段的值
			}
		</pre>
		<p>更有趣的方式是将返回的记录直接转化成具体类的对象，比如我们先定义User类：</p>
		<pre>
			class User{
				//居然不使用构造函数或访问函数也能初始化私有变量
				private $id;
				private $username;

				public getUsername() {
					return $this->username;
				}
			}
		</pre>
		<p>接下来就可以读取数据库记录并将其转换为新的User对象：</p>
		<pre>
			$result = $pdo->query('SELECT id, username FROM users');
			$result->setFetchMode(PDO::FETCH_CLASS, 'User');
			while($row = $results->fetch()) {
				echo $row->getUsername();
			}
		</pre>
		<p>这种方法大大利于基于对象的网站，PHP将返回的列名与类的属性名进行匹配，如果没有匹配的列，PHP会创建列名相同的public属性，因此，这个类中的id和username私有属性没有加下划线前缀，以和数据库保持一致。</p>
		<p>也可以不使用fetch()方法而直接使用query取得结果：</p>
		<pre>
			foreach($pdo->query(SQL) as $row) {
				//直接读取$row记录就可以了
			}
		</pre>
		<p>使用预定义语句将查询语句与具体数据分开传送，这样可以有更高的执行效率和安全管理，不必担心SQL注入。比如常规的查询：</p>
		<pre>$results = $pdo->query("SELECT * FROM users WHERE email='$email' AND password=SHA1('$password')");</pre>
		<p>写成预定义语句方式是这样：</p>
		<pre>
			$smdt = $pdo->prepare('SELECT * FROM users WHERE email=? AND password= SHA1(?)');	//注意，占位符不必用引号括起来
		</pre>
		<p>占位符是可选的，默认是使用？，也可以用“具名占位符”，比如：email=:email AND password=SHA1(:password),$pdo->prepare会返回一个PDOStatement对象，接下来，调用此对象的execute()方法即可：</p>
		<pre>
			$smdt->execute(array('me@example.com', 'mypassword');
			$smdt->execute(array(':email' => 'me@example.com', ':password'=>'password'));	//这是使用具名占位符的查询方式，这样可以不考虑顺序，以免弄错。
		</pre>
		<p><a href="add_task_prepare.php">使用预处理语句的示例</a>。 </p>
		<p>PHP5.0版本开始，内置了标准库（Standard PHP Library，SPL），里面有很多功能强大的类，在5.3版本后SPL成了PHP固定的组成部分。</p>
		<p>PHP5.4新增了一个名为SessionHandlerInterface的接口，确定了创建会话处理对象必须具备的功能，它这样使用：</p>
		<pre>
			class SessionHandler implements SessionHandlerInterface {
				//这个类需要实现接口定义的几个方法
				function open(){}
				function read(){}
				function write(){}
				function close(){}
				function destroy(){}
				function gc()
			}
			//在使用时需要这样：
			$sh = new SessinHandler();
			session_set_save_handler($sh, $true);
		</pre>
		<p>上面的这个方法可以取代第3章中将会话保存在数据库中的方法。下面介绍几个SPL中的常用类：</p>
		<p>SplFileInfo类，可以用来获取文件信息：$file = new SplFileInfo('filename');。创建成功后就可以使用它的内置方法：getBasename()/getExtension()/getSize()/getType()...。要操纵文件则需要使用SplFileObject类，这个类是SplFileInfo类的子类，所以可以使用SplFileInfo类中的所有方法，它额外提供了操纵文件的方法fget()和fwrite()等，因此它的初始化方式稍有不同：$file = new SplFileObject('filename', 'r');</p>
		<pre>
			try{
				$fp = new SplFileObject('data.txt', 'w');
				$fp->fwrite("This is a line of data.\n");
				unset($fp);
			} catch(Exception $e) {
				echo 'The process could not be completed because the script:' . $e->getMessage();
		</pre>
		<p>SPL定义了很多不同的异常类，每一个都是从Exception类派生的。如果方法没有收到正确的参数，可以让类抛出InvalidArgumentException。</p>
		<p>迭代器（iterator）是一种设计模式，它的作用是使所有复杂数据结构的组件都可以使用循环来访问。无论迭代什么样的数据结构，我们都可以使用foreach来访问。SPL定义了很多迭代器：ArrayIterator/RecursiveArrayIterator/LimitIterator/DirectoryIterator。比如：要对文件夹中的文件进行迭代，可以使用DirectoryIterator类：</p>
		<pre>
			$dir = new DirectoryIterator('.');
			foreach ($dir as $item) {
				//使用 $item 来访问，DirectoryIterator类每次都会返回一个SplFileObject对象。
			}
		</pre>
		<p>这些类都实现了Iterator接口，这个接口定义了以下方法：</p>
		<ul>
			<li>current() 返回当前数据项</li>
			<li>key() 返回当前数据项的键或者该项在列表中的位置</li>
			<li>next() 使用数据项键或者位置前移</li>
			<li>rewind() 重置键或者位置</li>
			<li>valid() 返回一个boolean变量，表明当前键或者位置是否指向数据值</li>
		</ul>
		<p>定义了以上方法之后，PHP就能知道该类类型的对象需要迭代。<a href="hinting.php">示例代码</a> </p>
		<p>SPL中也有定义新的数据结构的类，使用对象创建更有针对性的数组的变种。比如SplFixedArray类，它只有固定数量的元素，只能用大于或等于0的数字作为下标，不能用任何现有数组函数，但是它提供了比传统数组更好的性能。当然，它也必须实现Iterator接口。SPL的SplStack类则实现了后进先出的栈结构。SplQueue实现了队列。</p>
		<p>在SPL中用以下方式取代了__autoload()函数，实现了类的自动加载。它分为两步：</p>
		<pre>
			//首先，定义类的加载函数，类似于__autoload()
			function class_loader($class) {
				require('classes/'. $class . '.php';
			}
			//第二步，开启自动加载，即将加载函数（上面定义的名称是class_loader,也可以用其它名称）注册到PHP
			spl_autoloaded_register('class_loader');
		</pre>
		<p>SPL还定义了Countable接口，用于实现计数，它需要实现一个名为count的方法，它没有参数，返回值是整数：</p>
		<pre>
			class Department implements Iterator, Countable {
				//其它定义

				function count() {
					return count($this->_employees);
				}
			}
			//这样，就可以直接使用count函数来统计Department中的员工数量了
			$hr = new Department();
			..... //执行添加员工代码
			echo count($hr);	//直接用count函数统计
		</pre>
	</section>
	<footer>
		<p>虽然有点麻烦，尽量习惯使用预定义语句的方式吧。</p>
	</footer>
</article>
<article>
	<header>
		<h1>第9章 面向对象编程实践：CMS系统示例</h1>
		<section class="tips">
			<ul>
				<li></li>
				<li></li>
				<li></li>
				<li></li>
				<li></li>
			</ul>
		</section>
	</header>
	<section>
		<p>MVC架构既适合应用程序设计也适合网站设计，使用MVC架构系统会更具可扩展性，更易于维护。在网页环境下，模型通常代表数据库表格，也代表那些没有存储在数据库中的表单数据（比如在联系表单中所用的表单）。视图指的是HTML网页，即用户实际看到的最终动态生成的输出。控制器响应用户动作，比如页面请求或表单提交，还包括验证数据、将数据插入数据库、显示结果等逻辑操作。</p>
		<p>一般的MVC架构是使用类作为控制器，本例用PHP脚本，但是控制器中不应当包含或产生任何HTML代码。所有的HTML代码都应当保存在单独的视图文件中，视图文件中的PHP代码应当尽量少。</p>
		<p>在MVC中倾向于使用数据库的表来表示大部分模型。</p>
	</section>
	<footer>
		<p></p>
	</footer>
</article>
<article>
	<header>
		<h1>第10章 PHP网络编程</h1>
		<section class="tips">
			<ul>
				<li></li>
				<li></li>
				<li></li>
				<li></li>
				<li></li>
			</ul>
		</section>
	</header>
	<section>
		<p>对象的序列化serialize()函数可以将对象转换为字符串，这样就可以把对象保存在字符串中或数据库的字段里面，需要使用对象时再使用反序列化函数unserialize()函数即可还原，但是序列化函数不能保存对象中的方法，只能保存属性的类名，因此，反序列化时必须能访问所属类的定义才能使用其中的方法。在会话中保存对象时，PHP会自动将其序列化和反序列化。</p>
		<p>要使用能处理不同类型表单元素，具有验证数据的不同方式，能显式错误信息的类，可以使用HTML_QuickForm2，需要用PEAR安装，sudo pear install HTML_QuickForm2即可。它安装在Pear目录下，如果没有载入PEAR路径可以这样载入使用：</p>
		<pre>
			set_include_path(get_include_path() . PATH_SEPARATOR . '/usr/local/pear/share/pear/');
			require('HTML/QuickForm2.php');
			//要创建新表单只需这样
			$form = new HTML_QuickFOrm2('formName');	//默认的提交方式是post，也可以添加其他表单属性
		</pre>
		<p>向表单中添加元素的语法如下：$form->addElement('type', 'name', 'attributes', 'data');，也可以用变量来生成一个引用，方便以后的操作：只需要$element = $form->addElement(....)即可，然后就可以用$element直接引用元素对象，比如设置一个标签:$element->setLabel（‘Password：’);。</p>
		<p>除了HTML元素，HTML_QuickForm还定义了几个元素：</p>
		<ul>
			<li>date 用于选择日期的元素</li>
			<li>group 一组相关元素</li>
			<li>hierselect 两个级联输入域</li>
			<li>script 内联JavaScript</li>
			<li>static 文本或HTML块</li>
		</ul>
		<p>使用HTML_QuickForm的主要目的是验证和约束元素，具体用法<a href="http://pear.php.net/manual/en/package.html.html-quickform2.php">参考文档</a>。</p>
		<p>可以为元素添加过滤器，保证元素值的有效性，只需用$element->addFilter('functionName')；，可以用系统函数或自定义函数，比如$element->addFilter('trim');。</p>
		<p>同样可以给元素添加规则，保证元素值的合法性，只需$element->addRule('rule_type', 'error_message');。比如：$element->addRule('required', 'XX值不能为空‘);，这样来设置必填字段。而如果有length这样的规则则需要额外的参数,比如：$element->addRule('length', '请输入你的年龄', array('min'=>1, 'max'=>150));。可用的验证规则如下：</p>
		<ul>
			<li>compare 比较两个及更多元素</li>
			<li>email 必须在语法上合乎email地址规则</li>
			<li>eq 必须与某个值相等</li>
			<li>lt/gt 必须小于/大于某个值</li>
			<li>lte/gte 必须小于等于/大于等于某个值</li>
			<li>length 必须在给定的范围内，范围用数组array('min'=>number, 'max'=>number)表示</li>
			<li>maxlength/minlength 元素能够具有的最大/最小字符长度</li>
			<li>noempty 元素必须有一个非空的值</li>
			<li>required 必填元素</li>
		</ul>
		<p>也可以创建自己的规则来检查，比如邮件地址是否己注册等问题。</p>
		<p>最后要验证表单数据只需调用$form->validate()方法即可。所有元素都通过验证，这个方法将返回true。</p>
		<p>这一节演示了创建网站的实例，具体参照<a href="Standard%20Project/index.php">示例代码</a>的注释吧。</p>
	</section>
	<footer>
		<p>这个轻量级的网站结构感觉很好用，比框架更直接。</p>
	</footer>
</article>
<article>
	<header>
		<h1>第11章 PHP与服务器</h1>
		<section class="tips">
			<ul>
				<li></li>
				<li></li>
				<li></li>
				<li></li>
				<li></li>
			</ul>
		</section>
	</header>
	<section>

	</section>
	<footer>
		<p></p>
	</footer>
</article>
<article>
	<header>
		<h1>第12章 PHP的命令行界面</h1>
		<section class="tips">
			<ul>
				<li></li>
				<li></li>
				<li></li>
				<li></li>
				<li></li>
			</ul>
		</section>
	</header>
	<section>
	</section>
	<footer>
		<p></p>
	</footer>
</article>
<article>
	<header>
		<h1>第13章 XML和PHP</h1>
		<section class="tips">
			<ul>
				<li></li>
				<li></li>
				<li></li>
				<li></li>
				<li></li>
			</ul>
		</section>
	</header>
	<section>
	</section>
	<footer>
		<p></p>
	</footer>
</article>
<article>
	<header>
		<h1>第14章 PHP程序调试、测试与性能</h1>
		<section class="tips">
			<ul>
				<li></li>
				<li></li>
				<li></li>
				<li></li>
				<li></li>
			</ul>
		</section>
	</header>
	<section>
	</section>
	<footer>
		<p></p>
	</footer>
</article>
</body>
</html>